`timescale 1ns/1ps
`default_nettype none
//文件名：rtl/tb.v
//作用：CPU 测试用例
module tb;
  // -------- 1) 时钟/复位 --------
  reg clk  = 1'b0;
  reg rstn = 1'b0;
  always #5 clk = ~clk; // 100MHz

  // -------- 2) DUT --------
  // 顶层端口与工程一致
  cpu_top dut (
    .clk  (clk),
    .rstn (rstn)
  );

  // -------- 3) plusargs / 文件 --------
  reg [1023:0] imem_file = "prog_bram_min_words.hex";
  initial begin
    if ($value$plusargs("imem=%s", imem_file)) ;
    $display("TB: loading %0s into dut.u_imem.mem ...", imem_file);
    $readmemh(imem_file, dut.u_imem.mem);

    // 打印前 8 条 word，确认加载
    #1;
    $display("IMEM[0..7] words:");
    $display("  [0]=0x%08x", dut.u_imem.mem[0]);
    $display("  [1]=0x%08x", dut.u_imem.mem[1]);
    $display("  [2]=0x%08x", dut.u_imem.mem[2]);
    $display("  [3]=0x%08x", dut.u_imem.mem[3]);
    $display("  [4]=0x%08x", dut.u_imem.mem[4]);
    $display("  [5]=0x%08x", dut.u_imem.mem[5]);
    $display("  [6]=0x%08x", dut.u_imem.mem[6]);
    $display("  [7]=0x%08x", dut.u_imem.mem[7]);
  end

  // -------- 4) 波形 --------
  initial begin
    $dumpfile("tb.vcd");
    $dumpvars(0, tb_smoke);
  end

  // -------- 5) 复位流程 --------
  task wait_cycles(input integer n);
    integer k; begin for (k=0;k<n;k=k+1) @(posedge clk); end
  endtask

  initial begin
    rstn = 1'b0; wait_cycles(4);
    rstn = 1'b1;
  end

   // -------- 6) 选择场景：ALU / DMEM（修正版） --------
  reg [31:0] SCENE_ALU  = 32'h414C555F; // "ALU_"
  reg [31:0] SCENE_DMEM = 32'h444D454D; // "DMEM"
  reg [31:0] scene_sel  = 32'h414C555F; // 默认 ALU
  integer firstch;
  reg [1023:0] scene_str;

  initial begin
    // 1) 支持 +DMEM / +ALU 这类布尔 plusargs
    if ($test$plusargs("DMEM"))       scene_sel = SCENE_DMEM;
    else if ($test$plusargs("ALU"))   scene_sel = SCENE_ALU;
    // 2) 兼容 +scene=... ，只看首字符（%c）
    else if ($value$plusargs("scene=%c", firstch)) begin
      if (firstch=="D" || firstch=="d") scene_sel = SCENE_DMEM;
      else                              scene_sel = SCENE_ALU;
    end
    $display("TB: scene = %s", (scene_sel==SCENE_DMEM) ? "DMEM" : "ALU");
  end

  // -------- 7) 心跳 --------
  integer hb=0;
  always @(posedge clk) if (rstn) begin
    hb <= hb+1;
    if (hb%10000==0) $display("[HB %0d] IF.pc=0x%08x", hb, dut.if_pc);
  end

  // -------- 8) 断言检查 --------
  // 注意：寄存器堆跨层次路径：dut.u_rf.regs[0..31]
  // x0 恒为 0
  always @(posedge clk) if (rstn) begin
    if (dut.u_rf.regs[0] !== 32'd0) begin
      $fatal(1, "x0 must be 0, got=0x%08x", dut.u_rf.regs[0]);
    end
  end

  // 场景 ALU：等待若干拍后检查 x1..x4
  // 程序末尾有 jal x0,0 自旋；这里等 30 拍足够写回
  task check_alu;
  begin
    wait_cycles(30);
    if (dut.u_rf.regs[1] !== 32'd5)
      $fatal(1, "ALU: x1 exp=5, got=0x%08x",  dut.u_rf.regs[1]);
    if (dut.u_rf.regs[2] !== 32'd7)
      $fatal(1, "ALU: x2 exp=7, got=0x%08x",  dut.u_rf.regs[2]);
    if (dut.u_rf.regs[3] !== 32'd12)
      $fatal(1, "ALU: x3 exp=12, got=0x%08x", dut.u_rf.regs[3]);
    if (dut.u_rf.regs[4] !== 32'd7)
      $fatal(1, "ALU: x4 exp=7, got=0x%08x",  dut.u_rf.regs[4]);
    $display("[PASS] ALU smoke ok.");
    $finish;
  end
  endtask

  // 场景 DMEM：等待若干拍后检查访存相关寄存器
  // DMEM 基址 0x3000，程序写/读 0x3008，预期 x7==x8==0x11223344
  task check_dmem;
  begin
    wait_cycles(60);
    if (dut.u_rf.regs[5] !== 32'h00003008)
      $fatal(1, "DMEM: x5 (addr) exp=0x00003008, got=0x%08x", dut.u_rf.regs[5]);
    if (dut.u_rf.regs[7] !== 32'h11223344)
      $fatal(1, "DMEM: x7 (data) exp=0x11223344, got=0x%08x", dut.u_rf.regs[7]);
    if (dut.u_rf.regs[8] !== 32'h11223344)
      $fatal(1, "DMEM: x8 (loaded) exp=0x11223344, got=0x%08x", dut.u_rf.regs[8]);
    $display("[PASS] DMEM smoke ok.");
    $finish;
  end
  endtask

  initial begin
    @(posedge rstn);
    if (scene_sel==SCENE_DMEM) check_dmem();
    else                       check_alu();
  end
endmodule

`default_nettype wire
